Принципы объектно-ориентированного программирования

         

Сборки и развертывание

Именно благодаря операции развертывания тяжелая работа программиста становится доступной пользователю. Сборки .NET делают развертывание намного более простым и намного более надежным, чем традиционное развертывание Windows. Частное развертывание сборки столь же просто, как копирование компонента сборки в тот же самый каталог, в котором расположена клиентская программа. А общедоступное развертывание сборки регистрирует компонент с уникальным именем (известным как строгое имя) в глобальном кэше сборок, благодаря чему сборка становится доступной для общего использования.
Эта глава начинается с обсуждения понятий сборки, которая является основной единицей развертывания при частном развертывании сборки в .NET. Затем описано и общедоступное развертывание сборки. Управление версиями


и цифровое подписание сборок обсуждаются в контексте общедоступного развертывания. После этого на примере программы Hotel (Гостиница) демонстрируется инсталляция и приемы развертывания сборок. Наконец, представлены развертывание Visual Studio.NET и мастера установки. По ходу обсуждения мы иллюстрируем множество полезных инструментальных средств, которые являются частью комплекса инструментальных средств разработки программ .NET Framework SDK.

Сборки

Сборки положены в основу технологии компонентов .NET. Сборка — основная единица развертывания и управления разрешениями защиты, версиями, а также повторным использованием двоичного кода. Сборка содержит двоичный выполнимый код, составленный из команд управляемого промежуточного языка 1L (Intermediate Language), a также метаданных, которые полностью описывают содержимое сборки. Сборки могут также содержать данные ресурса и быть упакованы в виде динамически подключаемой библиотеки (DLL) или ЕХЕ-файлов. Сборка может быть составлена из одного или нескольких физических файлов на диске, но это — все равно одна логическая единица развертывания. Сборки, как говорят, описывают себя сами, так как они содержат в метаданных информацию о себе, и поэтому для их установки не требуется никакой внешней информации, например, в системном реестре. Это делает .NET-компоненты намного более простыми и менее подверженными ошибкам при установке и деинсталляции, чем традиционные компоненты, построенные на основе модели компонентных объектов Microsoft (СОМ), которые имели обширные требования к информации в системном реестре. Мало того, что сборки описывают себя сами, они также содержат и хэш-код, представляющий двоичное содержимое сборки. Этот хэш-код может использоваться для того, чтобы подтвердить подлинность и обнаружить тайные изменения или искажение сборок с цифровой подписью.
Прежде чем развернуть сборку в глобальном кэше сборок, ее сначала необходимо подписать в цифровой форме. Сборка с цифровой подписью имеет криптографически сгенерированную информацию, которую общеязыковая среда выполнения CLR может использовать для проверки, чтобы выполнить важные правила зависимости при размещении и загрузке сборок. Простая проверка версии гарантирует, что клиентская программа правильно использует общедоступные сборки с учетом тех их версий, которые были определены при первоначальной компиляции и тестировании клиента. Благодаря этому можно на самом деле устранить жуткую проблему "ада динамически подключаемых библиотек (DLL)", где клиенты и компоненты, построенные на основе модели компонентных объектов Microsoft (COM), могли легко "рассинхронизироваться" друг с другом, если старая версия заменялась более новой, — это нарушало работу существующих клиентов. Сборка с цифровой подписью может также проверить, что никакой неправомочный человек не изменил общедоступное содержимое сборки после того, как была поставлена цифровая подпись. Такая проверка гарантирует не только то, что вы не сможете случайно использовать неправильную версию, но также, что вы не будете злонамеренно обмануты в результате использования компонента, который мог бы причинить серьезный вред.
Хотя часто имеется взаимнооднозначное соответствие между пространством имен и сборкой, сборка может содержать множественные пространства имен, причем одно пространство имен может использоваться несколькими сборками. Точно так же часто существует взаимнооднозначное соответствие между сборкой и файлом двоичного кода (т.е. динамически подключаемой библиотекой (DLL) или исполняемым файлом). Однако одна сборка может содержать несколько файлов двоичного кода. Помните, что сборка — единица развертывания, пространства имен поддерживают иерархическую систему именования, а динамически подключаемая библиотека (DLL), или исполняемый файл— единица упаковки функциональных возможностей в пределах файловой системы. Важно также понимать разницу между сборкой, которая является единицей развертывания, и приложением, которое является единицей конфигурации.
Сборка без подписи идентифицируется просто удобочитаемым названием (именем), а также номером версии. Сборка с цифровой подписью идентифицируется также по имени ее создателя, однозначно определяемому по криптографической паре (ключей). При необходимости сборка может также включать код культуры для того, чтобы поддерживать специфические для данной культуры наборы символов и форматы строк.



Содержимое сборки

Утилита lldasm.exe может использоваться для просмотра содержимого сборки, чтобы лучше понять, как работают управление версиями, цифровое подписание и развертывание. Сначала нам понадобится сборка для экспериментирования. Здесь поможет компонент SimpleComponent, созданный с помощью Visual Studio.NET. Для его создания мы в диалоге New Project (Новый проект) выбрали шаблон managed C++ Class Library (Управляемый C++ на основе Библиотеки классов). Название проекта — SimpleComponent, а в следующих исходных файлах приведена его реализация, которая только немного изменяется в зависимости от кода, сгенерированного мастером. Вспомните, что важно объявить метод AddEmUp общедоступным, чтобы он был видим для кода вне данной сборки.

//Assemblylnfо.cpp
#include "stdafx.h" // имеет #using <mscorlib.dll>
using namespace System::Reflection;
// использование пространства имен Система::Отражение;
using namespace System::Runtime::CompilerServices;
// использование пространства имен
// Система::Время выполнения::CompilerServices;
[_assembly::AssemblyTitleAttribute("")];
[_assembly: : AssemblyDescriptionAttnbute ( "" ) ] ;
[_assembly::AssemblyConfigurationAttribute("")];
[_assembly: : AssernblyCornpanyAttribute ( "") ] ;
[_assembly::AssemblyProductAttribute("")];
[_assembly::AssemblyCopyrightAttribute("")];
[_assembly::AssemblyTrademarkAttribute("") ] ;
[_assembly::AssemblyCultureAttribute("")];
[_assembly::AssemblyVersionAttribute("1.0.*")];
[_assembly::AssemblyDelaySignAttribute(false) ] ;
[_assembly::AssemblyKeуFileAttribute("")];
t_assembly::AssemblyKeyNameAttribute("")];
//SimpleComponent.cpp
#include "stdafx.h" // имеет #using <mscorlib.dll>
#include "SimpleComponent.h"
// SimpleComponent.h
using namespace System;
// использование пространства имен Система;
namespace SimpleComponent
// пространство имен SimpleComponent
{
public _gc class SomeClass
// класс сборщика мусора SomeClass
{
public: // должен быть общедоступен, чтобы сборка могла
// предоставлять его для использования
int AddEmUp(int i, int j)
{
return i+j;
}
};
}

Как только вы создали сборку компонентов .NET, содержащую вышеупомянутый код и скомпоновали ее, вы можете с помощью Ildasm.exe просмотреть ее содержимое, включая декларацию. Декларация содержит следующую информацию о сборке:

сборка идентифицируется по имени, версии, культуре, и, при необходимости, по цифровой сигнатуре (подписи); список файлов, которые составляют содержимое сборки; список других сборок, от которых зависит данная сборка; список разрешений, требуемых сборкой для ее выполнения.

Все это можно увидеть с помощью утилиты Ildasm Чтобы ее запустить, выполните следующую команду Результат выполнения показан на рис. 7.1.

Ildasm SimpleComponent.dll

Чтобы увидеть декларацию сборки SimpleComponent, выполните двойной щелчок на узле MANIFEST (МАНИФЕСТ, ДЕКЛАРАЦИЯ), который виден на рис. 7.1. Информация из манифеста показана на рис. 7.2.
Декларация содержит информацию о зависимостях и содержимом сборки Видно, что декларация сборки SimpleComponent содержит, среди других, следующую внешнюю зависимость

.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 EO 89 )
.hash = (09 BB ВС 09 EF 6D 9B F4 F2 CC IB 55 76 A7 02 91
22 88 EF 77 )
.ver 1:0:2411:0
}

Инструкция метаданных .assembly extern mscorlib указывает, что сборка SimpleComponent использует (и поэтому зависит от нее) стандартную сборку mccorlib.dll, необходимую для всего управляемого кода Сборка mscorlib — общедоступная сборка, которую можно найти в каталоге \WINNT\Assembly с помощью Проводника Windows (Windows Explorer) Эта зависимость появляется в метаданных SimpleComponent.dll благодаря оператору fusing <mscorlib.dll> в первоначальном исходном тексте (Stdafxh) Если бы другой оператор #using добавлялся для другой сборки, например fusing <System WinForms.dll>, то декларация содержала бы также соответствующую инструкцию зависимости .assembly extern System. WinForms



Рис. 7.1. Ildasm вле показывает содержимое SimpleComponent.dll

Инструкция метаданных .publickeytoken = (В7 7А 5С 56 19 34 ЕО 89 ) указывает общедоступную лексему (маркер) открытого ключа, являющуюся хэш-кодом открытого ключа, который ставится в соответствие своему секретному ключу, принадлежащему автору сборки mscorlib Эта лексема открытого ключа на самом деле не может использоваться непосредственно, чтобы подтвердить подлинность автора mscorlib Однако первоначальный открытый ключ, указанный в декларации mscorlib может использоваться для того, чтобы математически проверить, что секретный ключ на самом деле совпадает с тем, который действительно применялся при цифровом подписании сборки mscorlib Поскольку mscorlib.dll создала Microsoft, лексема открытого ключа, приведенная выше, принадлежит Microsoft Конечно, соответствующий секретный ключ — тщательно охраняемая корпоративная тайна, и, как полагает большинство экспертов в области защиты, такой секретный ключ практически очень трудно определить по открытому ключу Однако нет никакой гарантии, что некий математический гений не найдет когда-нибудь хитроумный способ делать это проще.
Как мы вскоре увидим, инструкция . publickeytoken присутствует в декларации клиентской сборки только в случае, когда сборка, на которую есть ссылка, имеет цифровую подпись (На самом деле все сборки, предназначенные для общедоступного развертывания, должны иметь цифровую подпись) Microsoft подписала в цифровой форме стандартные сборки NET, такие KaKmscorlib.dll и System.WinForms.dll принадлежащими ей секретными ключами Именно поэтому лексема открытого ключа для многих общедоступных сборок, содержащихся в каталоге \WlNNT\Assembly, имеет то же самое повторяющееся значение Создаваемые другими производителями сборки с цифровой подписью подписаны их собственными, отличными от приведенного выше, секретными ключами, и они будут иметь отличную от приведенной выше лексему открытого ключа в декларациях их клиентской сборки Позже вы научитесь создавать ваши собственные криптографические пары секретного и открытого ключа и сможете подписывать собственные сборки цифровой подписью для их развертывания через глобальный кэш сборок.



Рис. 7.2. Ildasm. ехе показывает декларацию SimpleComponent.dll

Декларация .publickeytoken

Чтобы сэкономить память, декларация .publickeytoken содержит только самые младшие 8 байтов кэш-кода открытого ключа производителя (он состоит из 128 байтов), вычисленного с помощью алгоритма SHA1. Однако, несмотря на это, она все же может использоваться для довольно надежной проверки А вот декларация .publickey содержит полный открытый ключ. Конечно, она занимает больше места, но именно поэтому злодеям труднее найти секретный ключ, который соответствует полному открытому ключу.
Важно обратить внимание, что, хотя цифровой ключ уникален, он сам по себе не может идентифицировать фактического автора конкретного модуля Однако разработчик сборки может использовать утилиту signcode, чтобы добавить цифровое свидетельство, которое может идентифицировать издателя сборки А если зарегистрировать цифровое свидетельство у Certificate Authority (Полномочного свидетеля), например у VenSign, то пользователи смогут установить надежность источника.
Инструкция метаданных .hash = (09 ВВ ВС 09 ... 77 ) обеспечивает фиксированный размер представления хэш-кода двоичного содержимого mscor lib. dll Если бы содержимое изменилось, в результате изменился бы и этот хэш-код Несмотря на то, что хэш-код имеет компактное представление, он с высокой вероятностью характеризует сборку Поэтому вычисленный на основе первоначальных данных, он может использоваться для многих целей, включая обнаружение ошибок и проверку Хэш-код для сборки mscorlib, показанной выше, с высокой вероятностью характеризует двоичные данные сборки mscorlib Это означает, что, если бы содержимое mscor I ib. dll было изменено случайно, преднамеренно, или даже злонамеренно, то, с астрономически высокой вероятностью, новый хэш-код не совпадал бы со старым, и изменение было бы обнаружено по хэш-коду Как описано далее в разделе по цифровому подписанию сборок, секретный ключ используется для того, чтобы гарантировать, что только уполномоченный человек может зашифровать хэш-код, и это используется для подтверждения (проверки) подлинности всей сборки
Инструкция метаданных .ver 1:0:2411:0 указывает версию сборки mscorlib Формат спецификации этой версии— Major .Minor :Build:Revision (Главный Младший Компоновка Пересмотр) Через какое-то время, когда будут выпущены новые версии этой сборки, существующие клиенты, которые были скомпонованы так, чтобы использовать данную версию, продолжат использовать именно данную версию, по крайней мере те версии, у которых совпадают значения главного и младшего номера версий Более новые клиентские программы, конечно, смогут обратиться к более новым версиям этой сборки, поскольку именно для них станут доступными новые версии Старые и новые версии могут быть развернуты буквально рядом посредством кэша глобальных сборок, и быть одновременно доступны старым и новым клиентским программам Обратите внимание, что версия 1:0:2411:0, появляющаяся в клиентской декларации, принадлежит текущей версии сборки mscorlib и не связана с атрибутом версии 1.0.*, указанным в исходном тексте SimpleComponent Вскоре мы более подробно рассмотрим четыре поля, которые составляют номер версии, а также управление версиями сборки
До сих пор мы сосредотачивались на зависимостях, которые определены в декларации сборки SimpleComponent Теперь давайте подробнее рассмотрим информацию в декларации, описывающую компонент SimpleComponent, содержащийся в сборке. Обратите внимание, что эта сборка не имеет цифровой подписи, и поэтому не содержит информации о ее создателе (т е , открытый ключ она в себе не содержит)

.assembly SimpleComponent
{
.hash algorithm 0x00008004
.ver 1:0:584:39032
}

Директива .assembly

Директива . assembly объявляет декларацию и определяет, какой сборке принадлежит текущий модуль. В данном примере директива . assembly определяет SimpleComponent (в качестве имени сборки. Именно это имя (вместе с номером версии и, возможно, открытым ключом), а не имя динамически подключаемой библиотеки (DLL) или (.исполняемого файла, используется во время выполнения для определения принадлежности сборки. Обратите также внимание, что, если сборка подписана, то в |директиве .assembly будет определен параметр .publickey. Директива .assembly декже указывает, добавлялись ли какие-либо пользовательские атрибуты к метаданным. Инструкция метаданных .assembly SimpleComponent указывает, что имя ысборки — SimpleComponent. Имейте в виду, что это — не имя класса компонента в рЬборке, а само имя сборки.

Алгоритмы хэширования

Алгоритм хэширования— математическая функция, которая берет первоначальные Ввходные данные произвольной длины и генерирует хэш-код, также известный как щрофиль сообщения, который представляет собой двоичный результат установленной |длины Эффективная хэш-функция — односторонняя (однонаправленная) функция, |При использовании которой коллизии возникают очень редко, а ее результат имеет рЬтносительно маленькую установленную длину. Идеальная хэш-функция также легко вычислима. Односторонняя функция— функция, не имеющая такой обратной, с помощью которой можно было бы фактически быстро вычислить первоначальные. Данные по значению хэш-кода. Фраза "коллизии возникают очень редко" означает, что вероятность того, что по двум первоначально различным входным данным будет сгенерирован тот же самый хэш-код, является очень маленькой, и мала вероятность вычисления двух отличающихся входных данных, которые приводят к тому же самому рначению хэш-кода Известные алгоритмы хэширования MD5 и SHA1, как полагают, являются превосходным выбором для использования в цифровом подписании, и оба они поддерживаются в .NET.
Инструкция .ver 1:0:584:39032 указывает окончательную версию сборки SimpleComponent, которая определена частично атрибутом AssemblyVersionAttribute в исходном тексте компонента. Управление версиями более подробно описано в следующем подразделе.

Управление версиями сборки

Как мы только что видели, декларация сборки содержит версию сборки, а также версии каждой из сборок, от которых она зависит. Детальный набор правил, используемых общеязыковой средой выполнения CLR для того, чтобы определить зависимости версии, называют политикой управления версиями. Заданная по умолчанию политика управления версиями определена зависимостями, указанными в декларациях сборки, но при необходимости ее можно изменить в файле конфигурации приложения или в общесистемном файле конфигурации. Автоматическая проверка версии выполняется общеязыковой средой выполнения CLR только на сборках со строгими именами (то есть, на сборках с цифровой подписью). Однако, каждой сборке, независимо от того, как она развернута, должен быть назначен номер версии. Номер версии сборки состоит из следующих четырех полей.

Главная версия (Major version): Главные несовместимые изменения. Младшая версия (Minor version): Менее значительные несовместимые изменения. Номер компоновки (Build number): Обратно совместимые изменения, сделанные в ходе разработки. Пересмотр (Revision): Обратно совместимые изменения, сделанные в ходе текущего быстрого исправления (Quick Fix Engineering, QFE).

Вышеупомянутые соглашения относительно назначения каждого поля номера версии не предписаны общеязыковой средой выполнения CLR. Именно программист устанавливает эти или любые другие соглашения при проверке совместимости сборки и определении политики управления версиями в файле конфигурации, который мы обсудим позже в этой главе.
Традиционно изменение значения главного или младшего номера указывает явную несовместимость с предыдущей версией. Это используется при существенных изменениях в новом выпуске сборки, и существующие клиенты не могут использовать новую версию. Изменения номера компоновки подразумевают совместимость вниз, и этот номер обычно изменяется каждый раз при очередной компоновке сборки в ходе разработки. Совместимость вниз между номерами компоновки является намеренной; однако, этого, очевидно, не может гарантировать общеязыковая среда выполнения CLR, и потому данное свойство должно быть проверено. Изменение номера пересмотра относится к изменениям, сделанным в ходе текущего быстрого исправления (Quick Fix Engineering, QFE). Это поле обычно используется для срочного исправления, которое общеязыковой средой выполнения CLR считается обратно совместимым, если в файле конфигурации не указано иное назначение этого поля. И снова, общеязыковая среда выполнения CLR не может гарантировать, что изменение, сделанное в ходе текущего быстрого исправления (Quick Fix Engineering, QFE), на 100 процентов обратно совместимо, но совместимость вниз желательна и должна быть тщательно проверена.
Информация, относящаяся к версии, может быть определена в исходном тексте, в атрибуте _assembly::AssemblyVersionAttribute. Класс AssemblyVersionAttribute определен в пространстве имен System: :Runtime: :CompilerServices (Система:: Время выполнения::СотрПег5ешсе5). Если этот атрибут не используется, в декларации сборки по умолчанию задается номер версии 0.0.0.0, который, вообще говоря, является признаком небрежности. В проекте, созданном Мастером проектов на управляемом C++ на основе Библиотеки классов (managed C++ Class Library project wizard), исходный файл Assemblylnfo.cpp автоматически генерируется с версией 1.0.*, т.е. главная версия равна 1, младшая версия — 0, причем значения пересмотра и компоновки генерируются автоматически. Если изменить AssemblyVersionAttribute на, например, "1.1.0.0", как показано ниже, то номер версии, отображенный в декларации, изменится, и будет равен 1:1: 0:0.

//Assemblylnfо.срр
#using <mscorlib.dll>
[_assembly::AssemblyVersionAttribute("1.1.0.0")];

Чтобы не указывать значений пересмотра и компоновки, можно использовать символ звездочка (*). Когда вы вообще определяете какой-либо номер версии, вы должны, как минимум, определить главный номер. Если вы определяете только главный номер, остающиеся значения будут по умолчанию иметь значение нуль. Если вы определяете также младшее значение, то можете опустить оставшиеся поля, которые по умолчанию будут обнулены, или можете указать звездочку, тогда значения будут сгенерированы автоматически. Звездочка в данном случае означает, что значение компоновки будет равняться количеству дней, прошедших с 1 января 2000 года, а значение пересмотра будет установлено равным количеству секунд, прошедших с полуночи, деленному на 2. Если вы определяете значения главного и младшего номеров, а также номера компоновки, причем указываете звездочку для значения пересмотра, то только номер пересмотра будет равен количеству секунд, прошедшему с полуночи, деленному на 2. Когда все четыре поля указаны явно, все четыре значения будут отражены в декларации. Следующие примеры показывают правильные (допустимые) спецификации версии.

Определено в исходном тексте Записано в декларации
Ни одно поле 0:0:0:0
1 1:0:0:0
1.1 1:1:0:0
1.1. * 1:1:464:27461
1.1.43 1:1:43:0
1.1.43.* 1:1:43:29832
1.1.43.52 1:1:43:52

Если указать звездочку, то версия автоматически будет изменяться каждый раз при компоновке компонента; однако каждая новая версия считается обратно совместимой, так как главные и младшие номера не изменяются автоматически. Чтобы определить новую обратно несовместимую версию, вы должны явно изменить главный и/или младший номер версии.


Частное развертывание сборки

Частное развертывание сборки просто означает, что конечный пользователь копирует сборку в тот же самый каталог, что и клиентская программа, использующая ее. Не нужна никакая регистрация, и не требуется никакая причудливая инсталляционная программа. Кроме того, не требуется никакая очистка системного реестра, не нужна также и никакая программа деинсталляции для удаления компонента. Чтобы деинсталлировать сборку, просто удалите ее с жесткого диска.
Конечно, ни один программист из самоуважения никогда не поставит коммерческий компонент, который конечный пользователь должен вручную копировать или удалять какие-либо файлы подобным способом, даже если сделать это очень просто. Пользователи привыкли использовать формальную инсталляционную программу, так что она должна поставляться, даже если ее работа тривиальна. Однако, ручное копирование и удаление сборки — идеальный способ быстрого и безболезненного управления развертыванием реализаций при разработке, тестировании, отладке и испытании. Как вы помните, развертывание компонентов, построенных на основе модели компонентных объектов Microsoft (COM), никогда не было настолько просто. Ведь для развертывания требовался как минимум файл сценария, чтобы занести в системный реестр информацию о компоненте к клиентским программам и о среде времени выполнения модели компонентных объектов Microsoft (СОМ). С появлением сборок не нужно при инсталляции конфигурировать системный реестр, а, значит, позже, когда вы захотите отказаться от компонента, не нужно заботиться о том, чтобы тщательно стереть информацию из системного реестра.
Далее мы рассмотрим, как выполнить частное развертывание сборки для простого NET-компонента. В следующем разделе будет показано, как развернуть общедоступную сборку в глобальном кэше сборок.
Скомпоновав компонент сборки, можно создать клиентскую программу, которая вызывает общедоступные методы компонента Следующий код показывает пример такой клиентской программы, вызывающей общедоступный метод AddEmUp, приведенный ранее. Конечно, в случае необходимости, путь к сборке SimpleComponent.dll в инструкции fusing должен быть откорректирован, чтобы компилятор смог найти нужные данные. Кроме того, сборку нужно развернуть, чтобы общеязыковая среда выполнения CLR могла определить местонахождение сборки и загрузить ее во время выполнения. В этом подразделе развертывание достигается простым копированием сборки в каталог клиентской программы.

//SimpleComponentClient.срр
finclude "stdafx.h"
fusing <mscorlib.dll>
using namespace System;
// использование пространства имен Система;
fusing fusing <C:\OI\NetCpp\Chap7\SimpleComponent\Debug\
SimpleComponent.dll> using namespace SimpleComponent;
// использование пространства имен SimpleComponent;
void main()
{
SomeClass *psc = new SomeClass;
int sum = psc->AddEmUp(3, 4); // суммировать
Console::WriteLine(sum); // сумма
}

Как найти SimpleComponent?

Обратите внимание, что программа SimpleComponentClient имеет инструкцию fusing, указывающую компилятору, где найти сборку SimpleComponent, метаданные которой компилятор использует для контроля соответствия типов. Однако, если вы попробуете выполнить клиентскую программу, то возникнет исключение System. IQ.FileNotFoundException. Так получится потому, что загрузчик класса общеязыковой среды выполнения CLR неспособен найти сборку SimpleComponent. Чтобы выполнить клиент, нужно только скопировать сборку SimpleComponent в тот же самый каталог, где находится программа SimpleComponentClient.ехе.
Теперь можно рассмотреть и сравнить декларацию сборки этой клиентской программы, чтобы увидеть, как она взаимодействует с декларацией сборки SimpleComponent, приведенной ранее. Чтобы рассмотреть декларацию клиентской программы, используйте команду

Ildasm SimpleComponentClient.exe

Можно заметить, что декларация клиентской программы содержит следующую внешнюю зависимость от сборки SimpleComponent.

.assembly extern SimpleComponent
{
.hash = (2A 1C 2D D7 CA 9E 7E D5 08 5B DO 75 23 D3 50 76
5E 28 EA 31 )
.ver 1:0:584:39032
}

Из этого следует, что клиентская программа ожидает использовать сборку SimpleComponent с номером версии 1:0:584:39032. Однако, поскольку сборка развернута частным образом, на самом деле, когда клиент загружает эту сборку, принятая по умолчанию в общеязыковой среде выполнения CLR политика проверки значения версии проверку номера не выполняет. Просто ответственность за то, что в данном конкретном каталоге развернута нужная версия, возлагается на администратора или инсталляционную программу. Эту заданную по умолчанию политику проверки версии можно отменить, используя файл конфигурации. Хотя в клиентской декларации имеется хэш-код компонента, он фактически не используется общеязыковой средой выполнения CLR для того, чтобы проверить подлинность двоичного кода. И снова это происходит потому, что сборка развернута частным образом. В следующем разделе мы увидим, что номер версии и хэш-код используются автоматически для проверки содержимого кода общедоступных сборок, развернутых в глобальном кэше сборок.



Общедоступное развертывание сборки

Кэш сборки — средство параллельного ("бок о бок") развертывания (инсталляции) компонентов на всей машине. Термин "бок о бок" означает, что множество версий того же самого компонента могут постоянно находиться в кэше сборок рядом друг с другом. Глобальный кэш сборок содержит общедоступные сборки, которые являются глобально доступными для всех .NET-приложений на машине. Есть также кэш загружаемых сборок, доступный для приложений типа Internet Explorer, которые автоматически загружают сборки по сети. Чтобы развернуть сборку в глобальном кэше сборок, нужно создать для нее строгое имя.



Строгие имена

Гарантируется, что строгое имя будет глобально уникальным для любой версии любой сборки. Строгие имена генерируются тогда, когда сборка получает цифровую подпись. Это гарантирует, что строгое имя не только уникально, но и может быть сгенерировано только индивидуумом, который имеет секретный ключ.
Строгое имя состоит из простого текстового имени, открытого ключа и хэш-кода, зашифрованного соответствующим секретным ключом. Хэш-код также называется профилем сообщения, а зашифрованный хэш-код— цифровой подписью, электронной подписью и цифровой сигнатурой. Хэш-код фактически эффективно идентифицирует двоичное содержимое сборки, а цифровая сигнатура (подпись) фактически эффективно идентифицирует автора сборки. Все сборки, имеющие одно и то же строгое имя, считаются идентичными (при определении идентичности во внимание принимаются также номера версии). Сборки, строгие имена которых отличаются друг от друга, считаются различными. Полагают, что строгое имя является криптографически стойким, поскольку в противоположность простому текстовому имени, оно однозначно определит сборку на основании ее содержимого и секретного ключа ее автора. Строгое имя имеет следующие полезные свойства:

гарантирует уникальность, основанную на технологии кодирования; устанавливает уникальное пространство имен, основанное на использовании секретного ключа; препятствует неправомочному персоналу изменять сборку; не позволяет неправомочному персоналу управлять версиями сборки; позволяет общеязыковой среде выполнения CLR выполнять проверку содержимого кода общедоступных сборок.

Цифровые сигнатуры (подписи)

Если сборка должна быть развернута в глобальном кэше сборок, то необходимо, чтобы она имела цифровую подпись. Цифровая подпись (сигнатура) не требуется и не особенно полезна для сборки, развернутой частным образом, так как частная сборка развертывается пользователем для того, чтобы работать со своей определенной клиентской программой и потому согласована с ней. Если даже развернутая частным образом сборка имеет цифровую подпись, общеязыковая среда выполнения CLR по умолчанию не проверяет этого, когда загружает сборку для клиентской программы. Поэтому ответственность за предотвращение неправомочной модификации или подтасовки развернутых частным образом сборок полностью возложена на администратора. С другой стороны, очень выгодно использовать общедоступно развернутые (т.е. общедоступные) сборки, так как они должны быть подписаны в цифровой форме, и потому обычно их используют многие клиенты, причем рядом могут существовать несколько версий одной сборки.
Цифровые подписи (сигнатуры) основаны на криптографических методах, в которых применяются открытые ключи. В мире криптографии применяется два основных криптографических метода — симметричные шифры (общий ключ) и асимметричные шифры (открытый ключ). Симметричные шифры совместно используют секретный ключ как для кодирования, так и для расшифровки. Стандарт шифрования данных DES (Data Encryption Standard), Triple DES (Тройной стандарт шифрования данных) и RC2 — примеры симметричных алгоритмов шифрования. Симметричные шифры могут быть очень эффективными и мощными в обеспечении секретности сообщения между двумя доверяющими друг другу сотрудничающими лицами, но они не совсем подходят для ситуаций, где трудно совместно использовать секретный ключ. По этой причине симметричные шифры считаются неподходящими для цифровых подписей (сигнатур). Именно потому цифровые подписи (сигнатуры) используются не для секретности, а лишь для идентификации и опознавания, что является более открытым делом. Если бы вы совместно использовали ваш симметричный ключ с каждым, кто потенциально хотел бы убедиться в подлинности ваших сборок, то по неосторожности могли доверить его людям, которым захотелось исполнить вашу роль. Для использования в цифровых подписях (сигнатурах) намного лучше подходят асимметричные шифры.
Асимметричные шифры, которые также называются шифрами с открытым ключом, используют криптографическую пару открытого и секретного ключа. Ключи в паре математически связаны, и генерируются они вместе; однако чрезвычайно трудно вычислить один ключ по другому. Обычно открытый ключ выдается каждому, кто хотел бы убедиться в подлинности владельца сборки С другой стороны, владелец сохраняет соответствующий секретный ключ подписи в тайне, чтобы никто не мог подделать его подпись. Метод шифрования по схеме открытого ключа RSA (RSA-кодирование) — пример системы шифрования с открытым ключом.
Шифрование с открытым ключом основано на очень интересной математической схеме, которая позволяет зашифровать обычный текст одним ключом, а расшифровать, только зная ключ, соответствующий исходному Например, когда открытый ключ используется, чтобы зашифровать первоначальные данные (называемые открытым текстом), только соответствующий секретный ключ может помочь в расшифровке этого текста. Даже ключ, который использовался для шифрования, не поможет в расшифровке зашифрованного текста! Этот сценарий полезен тогда, когда секретные сообщения посылаются только человеку, который знает секретный ключ.
Рассмотрим теперь противоположный сценарий. Человек, который знает секретный ключ, использует его для того, чтобы зашифровать открытый текст. Получающийся зашифрованный текст не является тайной, так как каждый заинтересованный может получить открытый ключ, чтобы расшифровать данный текст. Этот сценарий бесполезен для сохранения тайны, но очень эффективен для опознавательных целей. Нет необходимости шифровать исходные данные полностью, поэтому, чтобы повысить эффективность, вместо них шифруется компактный хэш-код, который с высокой вероятностью характерен для исходных данных. Если вы получаете файл, который содержит зашифрованную версию его собственного хэш-кода, и расшифровываете его с помощью соответствующего открытого ключа, то фактически вы повторно вычислите хэш-код исходных данных. И если теперь вы обнаружите, что он совпадает с хэш-кодом, который был зашифрован, можете быть совершенно уверены, что именно владелец секретного ключа приложил цифровую подпись и данные не были изменены другими лицами Если предположить, что владелец сумел сохранить тайну секретного ключа, тогда совпадение результатов вычисления доказывает, что никто не смог бы исказить файл после того, как он был подписан в цифровой форме. На рис. 7.3 показано, как работает цифровая подпись (сигнатура).



Рис. 7.3. Вот так работает цифровая подпись (сигнатура)

Цифровое подписание методом шифрования по схеме открытого ключа RSA и SHA1

Чтобы подписать сборку, производитель вычисляет с помощью алгоритма SHA1 ее хэш-код (причем байты, зарезервированные для подписи (сигнатуры), предварительно обнуляются), и затем зашифровывает значение хэш-функции с помощью секретного ключа, используя метод шифрования по схеме открытого ключа RSA (RSA-кодирование). Открытый ключ и зашифрованный хэш-код сохраняются в метаданных сборки.



Цифровая подпись и развертывание общедоступной сборки

Чтобы развернуть сборку в глобальном кэше сборок, надо, чтобы она имела цифровую подпись. Разработчики могут разместить сборку в глобальном кэше сборок, используя утилиту Gacutil .exe (Global Assembly Cache utility), Проводник Windows (Windows Explorer) с расширением оболочки Windows посредством просмотра кэша сборок, или.
Инструмент администрирования NET (.NET Admin Tool). Инсталляция общедоступных сборок на конкретной машине конечного пользователя должна быть сделана с помощью программы установки компонентов (системы) по выбору пользователя.
Процесс цифрового подписания сборки включает генерацию криптографической пары открытого и секретного ключа, вычисление хэш-кода сборки, шифровку хэш-кода с помощью секретного ключа, и запись в сборку зашифрованного хэш-кода вместе с открытым ключом.
Зашифрованный хэш-код и открытый ключ вместе составляют полную цифровую сигнатуру (подпись). Обратите внимание, что цифровая подпись (сигнатура) записана в зарезервированную область сборки, не участвующую в вычислении хэш-кода. Когда все это сделано, сборка может быть развернута в глобальном кэше сборок (GAC). Все указанные шаги выполняются тремя простыми инструментальными средствами: утилитой Strong Name (Strong Name utility — Sn. exe), компоновщиком сборок (Assembly Linker — Al.exe) и утилитой Global Assembly Cache (Global Assembly Cache utility — Gacutil.exe). Чтобы скомпоновать, подписать в цифровой форме и развертывать общедоступную сборку, необходимо выполнить следующие шаги:
1. Разработать и скомпоновать компонент.
2. Сгенерировать пару открытого и секретного ключей.
3. Вычислить хэш-код содержимого сборки.
4. Зашифровать хэш-код, используя секретный ключ.
5. Поместить зашифрованный хэш-код в декларацию.
6. Поместить открытый ключ в декларацию.
7. Поместить сборку в глобальный кэш сборок.
Конечно, шаг 1 обычно выполняется с помощью Visual Studio.NET. Шаги 2 — 6 представляют собой цифровое подписание. Шаг 2 выполняется с помощью утилиты Strong Name— Sn.exe. Шаги 3—6 выполняются с помощью Visual Studio.NET или компоновщика сборок — утилиты Assembly Linking — Al. exe ("1" — это "эль", а не "один"). Чтобы на шаге 7 поместить сборку в глобальный кэш сборок, применяется утилита Global Assembly Cache— Gacutil.exe, Проводник Windows (Windows Explorer), или Инструмент администрирования .NET (.NET Admin Tool).
Сейчас мы опишем первый шаг— создание компонента. Для целей демонстрации мы используем пример, подобный предыдущему примеру сборки SimpleComponent, но он называется SharedComponent, и будет развернут в глобальном кэше сборок. Сначала должен быть создан новый проект SharedComponent; для этого используется шаблон Managed C++ Class Library (Управляемый C++ на основе Библиотеки классов), причем нужно добавить следующий код:

//SharedComponent.срр
#include "stdafx.h" // имеет fusing <mscorlib.dll>
#include "SharedComponent.h"
//SharedComponent.h
using namespace System;
// использование пространства имен Система;
namespace SharedComponent
// пространство имен SharedComponent
{
public _gc class SomeClass
// класс сборщика мусора SomeClass
{
public:
int AddEmUpdnt i, int j)
{
return i+j;
}
};
}

На следующем шаге нужно сгенерировать криптографическую пару. Генерацию криптографической пары можно выполнить с помощью утилиты Sn.exe. Эта утилита известна как утилита Strong Name, но иногда также называется и утилитой Shared Name, однако, этот последний термин теперь осуждается. Данный инструмент генерирует криптографически стойкое имя сборки. Вы генерируете криптографическую пару ключей (открытого и секретного) и размещаете ее в файле KeyPair.snk, как показано в следующей команде.

sn -k KeyPair.snk

Получающийся двоичный файл KeyPair. snk не предназначен для чтения пользователю. Но если вы любопытны, можете записать эти ключи в текстовый файл, в котором поля разделяются запятыми. Это делается с помощью следующей команды:

sn -о KeyPair.snk KeyPair.txt

Теперь можете рассмотреть полученный файл с помощью Блокнота (Notepad.exe); однако это не обязательно.
На следующем шаге нужно применить секретный ключ к сборке. Это может быть сделано при компиляции, что, конечно, является полезным для разработки и тестирования; однако, когда придет время промышленного выпуска сборки, нужно будет поместить "строку с названием компании, да и вообще придется использовать более формальный подход. Для защиты корпоративной цифровой подписи предприятия официальный секретный ключ держится в секрете и потому не может быть сообщен программисту. Вместо него программист может при разработке и проверке (испытании) использовать заменяющую криптографическую пару, которая применяется во время компиляции автоматически. Тогда перед выпуском сборки уполномоченный сотрудник ставит официальную корпоративную цифровую сигнатуру (подпись), используя секретный ключ, строго хранимый в тайне. Это делается после компиляции с помощью инструментального средства А1. ехе. Указанный инструмент будет описан в этом подразделе позже. Однако, чтобы во время компиляции применить цифровую сигнатуру (подпись) автоматически, вы просто используете определенный атрибут C++, как показано в следующем коде. В частности, обратите внимание, что файл KeyPair.snk, сгенерированный предварительно инструментом Sn. ехе, определен в атрибуте AssemblyKeyFileAttribute.

//Assemblylnfо.срр
#include "stdafx.h" // имеет fusing <mscorlib.dll>
using namespace System::Reflection;
// использование пространства имен Система::Отражение;
using namespace System::Runtime::CompilerServices;
// использование пространства имен
// Система::Время выполнения:: CompilerServices;
[assembly:AssemblyTitleAttribute(""}];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("")];
[assembly:AssemblyProductAttribute("")];
[assembly:AssemblyCopyrightAttribute("")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];
[assembly:AssemblyVersionAttribute("1.0.*") ];
[assembly:AssemblyDelaysignAttribute(false)] ;
[assembly:AssemblyKeyFileAttribute("KeyPair.snk") ];
[assembly:AssemblyKeyNameAttribute("")];

После добавления файла KeyPair. snk к AssemblyKeyFileAttribute, сборку нужно перетранслировать. Тогда при следующем запуске Ildasm. ехе покажет полученную в результате информацию, которая была включена в декларацию сборки для динамически подключаемой библиотеки (DLL) SharedComponent. Обратите внимание, что новая запись называется .publickey. В этой записи содержится открытый ключ создателя, который находится в файле KeyPair. snk. Именно этот открытый ключ может использоваться для расшифровки профиля сообщения, чтобы найти первоначальный хэш-код. Когда сборка развернута в глобальном кэше сборок, этот расшифрованный хэш-код сравнивается с новым, вычисленным по фактическому содержимому сборки, хэш-кодом. Такое сравнение позволяет определить, законна ли сборка (т.е. идентична оригиналу), или незаконна (т.е. разрушена или подделана). Конечно, когда вы используете Sn.exe, эта утилита сгенерирует другую криптографическую пару, и открытый ключ, приведенный ниже, будет отличаться от вашего.

.assembly SharedComponent
{
.publickey = (00 24 00 00 04 80 00 00 94 00 00 00 ...
... 56 5А Bl 97 D5 FF 39 5F 42 DF OF 90 7D D4 )
.hash algorithm 0x00008004
.ver 1:0:584:42238
}

Чтобы проверить разработанную нами сборку SharedComponent, мы должны создать испытательную клиентскую программу, а затем, вместо того, чтобы копировать SharedComponent.dll в каталог клиента, мы применим утилиту Gacutil.exe (Global Assembly Cache utility), или Проводник Windows (Windows Explorer), или Инструмент администрирования .NET (.NET Admin Tool), чтобы развернуть сборку в глобальном кэше сборок. Следующий код представляет собой испытательную клиентскую программу.

//SharedComponentClient.срр
linclude "stdafx.h"
#using <mscorlib.dll>
using namespace Systeir;
// использование пространства имен Система;
#using <C:\OI\NetCpp\Chap7\SharedComponent\Debug\
SharedComponent.dll>
using namespace SharedComponent;
// использование пространства имен SharedComponent;
using namespace System;
// использование пространства имен Система;
void main()
{
SomeClass *psc = new SomeClass;
int sum = psc->AddEmUp(3, 4); // суммируем
Console::WriteLine(sum); // сумма
}

Если бы вышеупомянутая испытательная клиентская программа была выполнена до инсталляции компонента-сборки, во время выполнения было бы запущено исключение FileNotFoundException, потому что сборку не удалось бы найти. Но на сей раз мы развертываем сборку в глобальном кэше сборок с помощью утилиты Gacutil.exe (Global Assembly Cache utility) Напомним, это только один из нескольких методов.

Gacutil -1 SharedComponent.dll

Затем можно выполнить клиент, причем теперь он должен работать надлежащим образом.



Рис. 7.4. Проводник Windows (Windows Explorer), показывает глобальный кэш сборок

После этого вы должны увидеть на консоли сообщение Assembly successfully added to the cache (Сборка успешно добавлена в кэш). В результате выполнения приведенной выше команды в каталоге \WINNT\Assembly был создан новый глобальный узел кэша сборки, имя этого узла — SharedComponent. Как видно на рис. 7.4, номер версии и создатель (т.е. лексема открытого ключа) сборки отображаются в Проводнике Windows (Windows Explorer)
Чтобы установить сборку в глобальный кэш сборок (GAC), можно также использовать Проводник Windows (Windows Explorer) — перетащите и опустите компонент в каталог Assembly. Кроме того, чтобы установить сборку в глобальный кэш сборок (GAC), можно использовать Инструмент администрирования .NET (.NET Admin Tool). Инструмент администрирования .NET (.NET Admin Tool) встроен в ММС и расположен в \WINNT\Microsoft.NET\Framework\vl.0.2914\mscorcfg.msc. Номер версии в каталоге будет отличен в более позднем выпуске .NET Framework. Хотя наличие третьего инструмента может показаться избыточным, эта встроенная в ММС утилита очень полезна, потому что упрощает решение многих задач На рис. 7.5 показано окно верхнего уровня данного инструмента. Чтобы с его помощью добавить сборку в глобальный кэш сборок (GAC), достаточно всего лишь выбрать Assembly Cache в левой области окна, щелкнуть правой кнопкой мыши, и выбрать Add (Добавить). В появившемся диалоговом окне переместитесь к нужному файлу, выберите сборку, которую хотите добавить, и щелкните на кнопке Open (Открыть).
Теперь, когда общедоступная сборка развернута в глобальном кэше сборок, клиентская программа SharedComponentClient может использовать нашу сборку. После того, как вы установили сборку в глобальном кэше сборок (GAC), вы можете переместить клиентскую программу в другой каталог. Вы можете выполнять клиента, причем нет необходимости перемещать сборку, установленную в глобальном кэше сборок (GAC).



Рис. 7.5. Инструмент администрирования .NET(.NETAdmin Tool) позволяет выполнять много административных функции .NET

Но что случается тогда, когда мы изменяем главную или младшую версию SharedComponent, или подписываем ее другим секретным ключом, а клиентскую программу SharedComponentClient не перекомпоновываем? Если сделать это со сборкой SimpleComponent, развернутой в частном порядке, то общеязыковая среда выполнения CLR не предпринимает никаких попыток обнаружить такое несоответствие (когда действует заданная по умолчанию политика) Однако, если сделать это с общедоступной сборкой, общеязыковая среда выполнения CLR запустит FileNotFoundException, когда обнаружит, что главная или младшая версия либо лексема открытого ключа отличается от того, что было установлено в соответствующем поле при компоновке клиента. Обратите внимание, что это исключение не будет вызвано, если реализация изменена, но главный и младший номер версии не изменены, а сборка подписана тем же самым секретным ключом.



Управление версиями общедоступных компонентов

Как мы видели ранее, чтобы создать новую версию сборки, нужно просто изменить номер версии, который состоит из следующих четырех полей: главный (major) номер, младший (minor) номер, номер компоновки (build) и номер пересмотра (revision). He забывайте, что поля главного и младшего номеров указывают изменения версии, которые не являются обратно совместимыми. Что случается, когда вы устанавливаете две несовместимые версии одной и той же сборки? Чтобы увидеть полученный эффект, попробуйте изменить главный или младший номер версии сборки SharedComponent, которую в предыдущем подразделе мы развернули в глобальном кэше сборок. Ранее была развернута версия 1.0.584.42238 Поэтому, если вы создадите новую версию, у которой младший номер заменен (т. е. версию 1:1 *.*), и развернете ее в глобальном кэше сборок, то с помощью Проводника Windows (Windows Explorer) вы увидите, что доступны обе версии — с различными номерами версий, естественно (рис. 7.6).



Рис. 7.6. Проводник Windows (Windows Explorer) показывает, что в глобальном кэше сборок рядом расположены разные версии одного и того же компонента



Подписание в цифровой форме после компиляции

С развертыванием общедоступных сборок связана еще одна проблема. Шаги, описанные выше, годились для цифрового подписания сборки во время компиляции, так как использовался атрибут, определяющий файл ключей, который содержал открытый и секретный ключи Но часто случается так, что ни один из программистов, разрабатывающих компоненты, которые будут упакованы в сборки, не знает секретного ключа всей организации. В этом случае при разработке и испытании программисты должны использовать временный секретный ключ, но когда придет время отправить сборку клиентам, назначенный представитель компании выполняет окончательное цифровое подписание, используя официальный секретный ключ организации, хранимый в строгой тайне. Это делается при помощи инструмента Sn.exe, с использованием опции -R, где "R" означает "resign" ("подписать заново"). Как правило, предварительно с помощью опции -k утилиты Sn.exe генерируется официальная криптографическая пара. Затем, используя опцию -R утилиты Sn. ехе. ставится подпись на каждой новой сборке.

sn -k TrueKeyPair.snk
sn -R SharedComponent.dll TrueKeyPair.snk

Хотя здесь две команды показаны вместе, обычно не нужно генерировать новую криптографическую пару каждый раз, когда вы заново подписываете очередную новую сборку. После второй команды на пульте появится сообщение Assembly 'SharedComponent.dll' successfully re-signed (Сборка 'SharedComponent.dll' успешно подписана заново).
Впрочем, есть и другой метод: вы можете задержать цифровое подписание. При создании сборки открытый ключ делается доступным компилятору, чтобы он мог поместить его в поле PublicKey декларации сборки. В файле резервируется место для подписи (сигнатуры), но сама подпись (сигнатура) не генерируется. Когда в конечном счете будет сгенерирована фактическая подпись (сигнатура), она помешается в файл с помощью опции -R утилиты Strong Name (sn.exe).
Чтобы указать компилятору, что вы хотите использовать отсроченное цифровое подписание, вы указываете значение true (истина) для атрибута AssemblyDelaySignAttribute в исходном тексте. Вы также должны включить открытый ключ с помощью атрибута

AssemblyKeyFileAttribute.

Предположим, что вы сгенерировали криптографическую пару открытого и секретного ключа так, как описано выше. Тогда вы с помощью опции -р утилиты Strong Name можете получить только открытый ключ, все еще сохраняя в тайне секретный ключ.

sn -p TrueKeyPair.snk PublicKey.snk

Затем вы добавляете следующие два атрибута к вашему коду:

[assembly:AssemblyDelaySignAttribute(true)];
[assembly:AssemblyKeyFileAttribute("PublicKey.snk")];

Сборка все еще не имеет правильной подписи (сигнатуры). Вы не сможете установить ее в глобальном кэше сборок или загрузить ее из каталога приложения. Вы можете отключить проверку подписи (сигнатуры) конкретной сборки, используя опцию -Vr утилиты Strong Name.

sn -Vr SharedComponent.dll

Перед поставкой сборку надо снабдить правильной сигнатурой (подписью), чтобы она могла быть развернута как общедоступная сборка. Для этого используйте опцию -R утилиты Strong Name и запишите криптографическую пару открытого и секретного ключей.

sn -R SharedComponent.dll TrueKeyPair.snk



Конфигурация сборки

Когда во время выполнения встречается статическая или динамическая ссылка на сборку, общеязыковая среда выполнения CLR осуществляет связывание со сборкой. Статическая ссылка определяется постоянным (статическим) образом в клиентской декларации сборки еще при ее компиляции. Динамическая ссылка генерируется программой во время выполнения, например, при вызове метода System.Reflection. Assembly.Load (Система.Отражение.Сборка.Загрузка). В любом случае связывание выполняется тем же самым способом для статической или динамической ссылки на сборку со строгим именем. Кроме простого имени, версия, открытый ключ (если он есть) и культура (если она есть) также отличают общедоступную сборку. Для связывания необходимо, как минимум, простое имя.
Процесс связывания с общедоступной сборкой часто называется зондированием или исследованием, которое является не чем иным, как набором эвристических правил, используемых общеязыковой средой выполнения CLR для того, чтобы определить местонахождение сборки и загрузить ее по требованию. Эти правила применяются только к ссылке на сборку со строгим именем, а ссылки на неподписанные (т.е. без цифровой подписи) частные сборки разрешаются непосредственно, т.е. без исследования или проверки версии (мы предполагаем, что действует заданная по умолчанию политика).
Однако можно использовать сборку со строгим именем, чтобы вынудить клиента связываться с определенной версией сборки. Предположим, что вы хотите допустить соответствие для нескольких обратно совместимых сборок (т.е. чтобы несколько сборок удовлетворяли условиям загрузки сборки общеязыковой среды выполнения CLR). Вы можете использовать XML-файл конфигурации, чтобы определить правила, используемые общеязыковой средой выполнения CLR для нахождения соответствующей сборки. Инструмент Администрирования .NET (.NET Admin Tool) позволяет создавать и поддерживать такие файлы с помощью графического интерфейса пользователя (GUI).
Имя файла конфигурации представляет собой имя клиентской программы, в конец которого добавлено расширение . conf ig. Файл конфигурации затем помещается в тот же самый каталог, где находится выполняемая клиентская программа.
В дополнение к файлу конфигурации приложения, существует административный файл конфигурации Machine .config. Он находится в подкаталоге Config того каталога, где установлена среда времени выполнения .NET (\WINNT\Microsoft.NET\ FrameworkA vl. О . 2914\, где номер версии отражает текущую компоновку .NET). Политика администрирования версии определяется теми же дескрипторами XML, что используются и в файле конфигурации приложения. Однако файл конфигурации администратора отменяет любые соответствующие параметры в файле конфигурации приложения.



Проводимая по умолчанию политика управления версиями

При связывании с общедоступно развернутыми сборками, в отсутствие любого файла конфигурации политики управления версиями, связывание просто использует информацию в декларации сборки для того, чтобы во время выполнения определить местонахождение других сборок и загрузить их по требованию. По умолчанию используются только поля главного и младшего номера версии, причем главный и младший номера должны совпадать точно, а затем берутся наиболее поздняя компоновка и пересмотр из доступных. Именно это чаще всего и требуется, так как клиентская сборка определяет зависимости от других сборок на основании главных и младших номеров версии, которые были установлены при компиляции клиентской сборки. Поскольку неизменные главные и младшие значения традиционно указывают совместимость вниз, а измененная компоновка и пересмотр указывают обратно совместимые исправления и изменения, то часто имеет смысл именно эта заданная по умолчанию политика управления версиями. Эта заданная по умолчанию политика управления версиями называется также Автоматической политикой QFE (Automatic QFE policy). При необходимости в файле конфигурации политики управления версиями это поведение может быть отменено, например для того, чтобы номера компоновки и пересмотра совпадали точно. Как это сделать, описано в следующем подразделе.



Файлы конфигурации политики управления версиями

Заданная по умолчанию политика управления версиями часто оказывает желательное действие, причем она действует автоматически, если не существует никакого файла конфигурации. Однако иногда требуется более тонкое управление процессом связывания с общедоступной сборкой. Как упоминалось ранее, чтобы сделать это для выполняемых клиентских программ, нужно создать файл конфигурации, содержащий XML-документ с тем же самым именем, что и у клиента, но с расширением . conf ig, и поместить его в тот же самый каталог, где находится клиентская выполняемая программа. Это может быть полезно в тех случаях, когда вы хотите отключить заданную по умолчанию автоматическую политику QFE, или если вы хотите использовать версию компонента-сборки, отличную от той, которая была указана при компиляции клиентской программы. Важно обратить внимание, что файл конфигурации приложения может затронуть поведение связывания только единственного приложения-клиента. Он не может быть применен ко всем клиентским программам, использующим данную сборку.
Для .NET файлов конфигурации тэгом верхнего уровня является <configuration> (<конфигурация>). Информация о связывании сборки находится в разделе <runtime> (<во время выполнения>). Пример файла конфигурации может выглядеть следующим образом:

<?xml version="l.0"?> <!-- версия -->
<configuration> <!-- конфигурация -->
<runtime> <!—во время выполнения -->
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.vl">
<dependentAssembly>
<assemblyldentity name="Customer"
<!-- Клиент -->
publicKeyToken="8bOe612d60bdeOca" />
<bindingRedirect oldVersion="l.0.0.0-1.1.0.0"
newVersion="l.1.0.0" />
</dependentAssemblу>
</assemblyBinding>
</runtime>
<!--во время выполнения -->
</configuration>
<!-- конфигурация -->

Правила, определяющие политику управления версиями, находятся в разделе <assemblyBinding>. XML-спецификация пространства имен необходима. Каждая сборка, политику управления версиями которой мы хотим установить, помещается в ее собственный раздел <dependentAssembly>. Элемент assemblyldentity имеет атрибуты, определяющие сборку, к которой этот раздел относится. Атрибут name (имя) обязателен; атрибуты culture (культура) и publicKeyToken являются необязательными. Атрибуты элемента bindingRedirect определяют то, что версии могут отобразить на другую версию. Атрибутом oldVersion может быть диапазон; атрибут newVersion может быть установлен только на одну версию. В вышеупомянутом примере любые ссылки на версии от 1.0.0.0 до 1.1.0.0 могут быть разрешены с помощью версии 1.1.0.0. Другими словами, версия 1.1.0.0 обратно совместима со всеми указанными версиями. Можно определить несколько элементов bindingRedirect.
Для установки файла конфигурации можно использовать Инструмент администрирования .NET (.NET Admin Tool). Чтобы запустить этот инструмент, дважды щелкните на \WINNT\Microsoft.NET\Framework\vl.0.2914\mscorcfg.msc в Проводнике Windows (Windows Explorer). Затем добавьте приложение к инструменту, щелкнув правой кнопкой мыши на Applications (Приложения) в левой области окна, и выберите Add (Добавить) из контекстного меню. Переместитесь к приложению, которое вы хотите конфигурировать.
Выберите его и щелкните на кнопке Open (Открыть). На рис. 7.7 показан Инструмент администрирования (Admin Tool) после того, как выполнены все указания.



Рис. 7.7. Обычно для конфигурирования политики управления версиями используется Инструмент администрирования .NET (.NET Admin Tool)



Обнаружение физического местоположения сборки

Мы выяснили, откуда общеязыковая среда выполнения CLR знает, какие версии сборки удовлетворят условиям разрешения ссылки. Но откуда общеязыковая среда выполнения CLR узнает, где сборка постоянно хранится на диске? Если сборка с нужной версией была загружена предварительно потому, что ранее в программе уже встретилась другая ссылка на эту сборку, то эта сборка уже используется. Если сборка имеет строгое имя, то проверяется кэш сборки; если в нем находится правильная версия, то используется найденная там сборка.
В файле конфигурации есть несколько элементов, которые можно определить для того, чтобы указать общеязыковой среде выполнения CLR, где нужно попробовать найти сборку.
Если сборка еще не найдена, выполняются динамические проверки, чтобы увидеть, была ли определена в файле конфигурации кодовая страница. В разделе <dependentAssembly> можно определить элемент <codeBase> (<кодовая страница>). Этот элемент имеет два атрибута, — version (версия) и URI (Uniform Resource Identifier — универсальный идентификатор ресурса (вариант унифицированного указателя информационного ресурса (URL))), — используемых с целью проверки сборки. Для установки этих элементов в файле конфигурации может использоваться вкладка Codebases (Кодовые страницы) диалога свойств сборки Инструмента администрирования .NET (.NET Admin Tool). Примеры этого элемента:

<codeBase version="l.1.1.1" href="http://www.abc.com/
Customer.dll" />
<codeBase version="l.1.1.2" href="flie:///c:\AcmeGui\
Customer.dll" />

Чтобы использовать элемент Codebase (Кодовая страница) вне каталога приложения или подкаталогов, требуется строгое имя. Если это условие не удовлетворяется, независимо от того, действительно ли найдена требуемая сборка, процесс связывания останавливается. Если сборка не найдена, генерируется исключение.
Если элемент Codebase (Кодовая страница) не был найден в файле конфигурации, среда времени выполнения продолжает исследовать сборку Теперь весь поиск ведется относительно каталога, в котором выполняется приложение. Такой каталог называется базой (ядром, основанием или уровнем отсчета) приложения (application base)
Сначала среда времени выполнения ищет в ядре приложения. Затем она смотрит в любых подкаталогах ядра приложения, которые имеют то же самое имя, что и сборка. Если в запросе определена культура, то среда времени выполнения ищет только подкаталог сборки в подкаталоге с названием (именем) требуемой культуры.
Наконец, в разделе assemblyBinding файла конфигурации можно определить атрибут privatePath. Этот атрибут представляет собой список подкаталогов ядра приложения. Подкаталоги в этом списке отделяются точкой с запятой, именно в них и осуществляется поиск.

<probing privatePath="\bin;\assemb" />

Атрибут privatePath можно также установить с помощью Инструмента администрирования .NET (.NET Admin Tool) на вкладке свойств приложения.



Многомодульные, или мультимодульные сборки

Сборка может быть составлена из нескольких модулей. Модуль — динамически подключаемая библиотека (DLL) (или файл с расширением ЕХЕ), который содержит управляемый код плюс метаданные, а также, возможно (но не обязательно'), декларацию. Однако сборка должна иметь одну и только одну декларацию, так что сборка может содержать несколько модулей, но только один модуль содержит декларацию, в которой хранится информация о содержимом всех модулей в сборке Модуль с декларацией может иметь или только декларацию, или содержать также и код, и ресурсы
Основное преимущество разбиения большой сборки на модули состоит в том, что каждый модуль содержится в отдельном файле динамически подключаемой библиотеки (DLL). Благодаря этому по сети можно загружать только требуемые модули. Это позволяет оптимизировать потребление памяти и ускорить работу (выполнение программ). Даже в локальном сценарии, чтобы повысить эффективность, общеязыковая среда выполнения CLR загружает классы на локальной машине со степенью детализации модуля Другая причина создания сборок с несколькими модулями состоит в том, что каждую часть такой сборки можно написать на своем языке NET. Чтобы скомпоновать сборку, которая содержит несколько модулей, необходимо скомпоновать каждый модуль отдельно, а затем объединить их утилитой А1. ехе.
Есть два способа создания мультимодульной сборки. Один способ состоит в том, чтобы создать все модули без деклараций, а затем создать один дополнительный модуль, который содержит только декларацию для всей сборки, но фактически не содержит никакого кода. Другая методика полагается на то, что только один модуль в сборке должен содержать и код, и декларацию для всей сборки, а все другие модули содержат только код без декларации. Мы опишем первую из этих альтернатив, так как она более симметрична и ее проще визуализировать. Вторая альтернатива здесь не описана; однако реализовать ее можно подобным способом, теми же самыми инструментальными средствами.
Сначала мы создаем два модуля без декларации, используя Visual C++ с управляемыми расширениями. Затем создаем третий модуль с декларацией, содержащей информацию о двух других модулях, используя инструмент Assembly Linker (Компоновщик сборки) — А1. ехе. Вместе эти три модуля образуют сборку. На рис. 7.8 показана структура полученной таким образом сборки.



Рис. 7.8. Мультимодульная сборка с явно выделенным модулем

Следующие исходные файлы составляют первый модуль без декларации. Этот модуль содержит общедоступный класс ClassInModuleWithoutManifestl, который предоставляет для использования общедоступный метод SomeMethod. Вы можете создать его с помощью шаблона Managed C++ Class Library (Управляемый C++ на основе Библиотеки классов).

//ModuleWithoutManifestl.h
using namespace System;
// использование пространства имен Система;
namespace ModuleWithoutManifestl
// пространство имен ModuleWithoutManifestl
{
public _gc class ClassInModuleWithoutManifestl
// класс сборщика мусора
ClassInModuleWithoutManifestl
{
public:
void SomeMethod();
};
}
//ModuleWithoutManifestl.cpp
finclude "stdafx.h"
// имеет
fusing <mscorlib.dll>
using namespace System;
// использование пространства имен Система;
tinclude "ModuleWithoutManifestl.h"
namespace ModuleWithoutManifestl
// пространство имен
ModuleWithoutManifestl
{
void CiassInModuleWithoutManifestl::SomeMethod()
{
Console::WriteLine(
"CiassInModuleWithoutManifestl::SomeMethod");
}
}

Опция компилятора /CLR:noAssembly указывает, что генерируется управляемый код, но декларация для сборки не генерируется. Опция компилятора /с подавляет автоматическое связывание, так как мы выполним его отдельной командой. Опция связывания /NOASSEMBLY также запрещает генерацию информации, относящейся к декларации. Поэтому чтобы сгенерировать первый модуль (динамически подключаемую библиотеку (DLL)) без декларации, в приглашении к вводу команды используются следующие две команды:

cl /CLR:noAssembly /с ModuleWithoutManifestl.срр
link /NOASSEMBLY /DLL ModuleWithoutManifestl.obj

Если вы предпочитаете создавать модуль в Visual Studio.NET, сначала для проекта нужно изменить опции компилятора и компоновщика, чтобы не генерировать информацию, относящуюся к декларации, и не добавлять ее к модулю. Для установки опции компилятора /CLR: noAssembly в Visual Studio.NET сделайте следующее:
1. Откройте диалоговое окно Property Pages (Страницы свойств) проекта. Чтобы сделать это, щелкните правой кнопкой мыши на узле ModuleWithoutManifestl, находящемся под узлом решения в окне Class View.
2. Откройте папку Configuration Properties (Свойства конфигурации).
3. Откройте папку C/C++.
4. Щелкните на странице General (Общая), находящейся под папкой C/C++.
5. Установите MetaData Only (/CLR: noAssembly) (Только метаданные) в качестве значения свойства Compile as managed (Компилировать как управляемый).
Объектный модуль, полученный в результате компиляции с такими параметрами, теперь нужно скомпоновать в Visual Studio.NET с опцией компоновщика /NOASSEMBLY. Эта опция компоновщика требует, чтобы вы установили также опцию компоновщика /NOENTRY. Чтобы внести эти изменения в проект, выполните следующее:
6. Откройте диалоговое окно Property Pages (Страницы свойств) проекта, если только оно не открыто.
7. Откройте папку Configuration Properties (Свойства конфигурации), если она не открыта.
8. Откройте папку Linker (Компоновщик).
9. Щелкните на Advanced page (Дополнительная страница) под папкой Linker (Компоновщик).
10. Установите Yes (/NOASSEMBLY) (Да) в качестве значения свойства Turn Off Assembly Generation (Выключить генерацию сборки).
11. Оставаясь на странице Advanced page (Дополнительная страница), установите Yes (/NOENTRY) (Да) в качестве значения свойства Resource Only DLL (Динамически подключаемая библиотека (DLL) только с ресурсами).
Теперь можно создать проект обычным способом.
Второй модуль— динамически подключаемая библиотека (DLL), названная ClassInModuleWithManifest2 .dll, — также будет создан без декларации. Следующие исходные файлы, которые очень похожи на приведенные выше исходные файлы первого модуля, составляют второй модуль без декларации. Он содержит общедоступный класс ClassInModuleWithoutManifest2, который предоставляет для использования общедоступный метод Some-OtherMethod. Этот проект также может быть создан в Visual Studio.NET с помощью шаблона Managed C++ Class Library (Управляемый C++ на основе Библиотеки классов).

//ModuleWithoutManifest2.h
using namespace System;
// использование пространства имен Система;
namespace ModuleWithoutManifest2
// пространство имен
ModuleWithoutManifest2
{
public _gc class ClassInModuleWithoutManifest2
// класс сборщика мусора ClassInModuleWithoutManifest2
{
public:
void SomeOtherMethod();
};
}
//ModuleWithoutManifest2.cpp
#include "stdafx.h" // имеет #using <mscorlib.dll>
using namespace System;
// использование пространства имен Система;
#include "ModuleWithoutManifest2.h"
namespace ModuleWithoutManifest2
// пространство имен ModuleWithoutManifest2
{
void ClassInModuleWithoutManifest2::SomeOtherMethod()
{
Console::WriteLine(
"ClassInModuleWithoutManifest2::SomeOtherMethod");
}
}

Этот модуль также не будет иметь декларации, и он создается с помощью следующих команд, которые фактически совпадают с теми, что использовались для создания первого модуля:

cl /CLFUnoAssembly /с ModuleWithoutManifest2.срр
link /NOASSEMBLY /DLL ModuleWithoutManifest2.obj

И снова можно выполнить те же самые шаги, что и для предыдущего модуля, если вы хотите создать этот модуль с помощью Visual Studio.NET. Только не забудьте установить MetaData Only (/CLR: noAssembly) (Только метаданные) в качестве значения свойства Compile as managed (Компилировать как управляемый), a Yes (/NOASSEMBLY) (Да)— в качестве значения свойства Turn Off Assembly Generation (Выключить генерацию сборки) и Yes (/NOENTRY) (Да)— в качестве значения свойства Resource Only DLL (Динамически подключаемая библиотека (DLL) только с ресурсами).
Наконец, эти два модуля должны быть помещены в ту же самую сборку. Обратите внимание, что физически они не связаны вместе. Они остаются различными файлами динамически подключаемых библиотек (DLL), но помещаются в объединенную логическую сборку. Для этого используется утилита А1. exe (Assembly Linker), которая включает информацию, содержащуюся в метаданных обоих модулей, в одну декларацию, содержащуюся в третьей динамически подключаемой библиотеке (DLL) — Combined-Assembly . dll. Чтобы сделать это, скопируйте два существующих модуля в общий каталог CombinedAssernbly, и введите следующую команду:

Аl ModuleWithoutManifestl.dll, ModuleWithoutManifest2.dll /
out:CombinedAssembly.dll

Теперь мы закончили создание мультимодульной сборки. Декларация сборки содержится в CombinedAssembly.dll. Чтобы это проверить, мы должны создать испытательную клиентскую программу. Обратите внимание, что все модули должны быть доступны для клиента. Этого можно достичь с помощью локального или общедоступного развертывания. Чтобы сделать этот пример простым, мы развернем сборку локальным образом, т.е. только скопируем три модуля (три динамически подключаемые библиотеки (DLL)) в ту же самую папку, в которой находится испытательная клиентская программа. Следующий код реализует испытательную клиентскую программу. Обратите внимание, что клиент ссылается только на одну динамически подключаемую библиотеку (DLL) сборки — на CombinedAssembly. dll, так как только она содержит декларацию. Если теперь вы создадите эту клиентскую программу и скопируете все три динамически подключаемые библиотеки (DLL), то увидите, что созданная клиентская программа успешно вызывает методы, определенные в классах, которые определены в отдельных модулях (динамически подключаемых библиотеках (DLL)). Однако ссылки есть только на одну сборку — на CombinedAssembly. dll. Это доказывает, что все три модуля работают как единая логическая сборка.

//MultiModuleAssemblyTestClient.срр
#include "stdafx.h"
#using <mscorlib.dll>
#using <C:\OI\NetCpp\Chap7\CombinedAssembly\
CombinedAssembly.dll>
using namespace ModuleWithoutManifestl;
// использование пространства имен
ModuleWithoutManifestl;
using namespace ModuleWithoutManifest2;
// использование пространства имен
ModuleWithoutManifest2;
void main()
{
ClassInModuleWithoutManifestl *pl =
new ClassInModuleWithoutManifestl;
pl->SomeMethod();
ClassInModuleWithoutManifest2 *p2 =
new ClassInModuleWithoutManifest2 ;
p2->SomeOtherMethod();
}



Инсталляция примера программной системы

Возможно, вы помните, что в предыдущей главе пример программы был реализован на С#, а не на управляемом C++, так как в нем было много кода графического интерфейса пользователя, который намного лучше реализуется на С#, чем на C++. Для целей данной главы все это вполне бы пригодилось, так как процесс развертывания одинаков для сборок, написанных на любом языке .NET, включая С# и управляемый C++. Однако проект был частично преобразован (конвертирован) из С# в управляемый C++. На данном этапе изучения примера мы разбили нашу программу администратора гостиницы (Hotel Administrator) на три сборки. В каталоге CaseStudy для этой главы имеется прикладная программа AcmeGui (файл с расширением ЕХЕ), написанная на С#, и два компонента (динамически подключаемые библиотеки (DLL)) сборки — Customer (Клиент) и Hotel (Гостиница), которые были реализованы на управляемом C++. Часть, написанная на С#, посвящена формам (т.е. функциональным возможностям графического интерфейса пользователя (GUI)). Код неграфического интерфейса пользователя, связанный с классами Customer (Клиент) и Hotel (Гостиница), а также связанные с ним классы, интерфейсы и структуры, в предыдущей версии примера программы были перемещены в новые сборки, представляющие собой динамически подключаемые библиотеки (DLL), написанные на управляемом C++.
На рис. 7.9 видно, как Solution Explorer (Поиск решения, Проводник решения) показывает, что проект AcmeGui имеет ссылки на сборки (динамически подключаемые библиотеки (DLL)) Customer (Клиент) и Hotel (Гостиница). Эти ссылки дают возможность компилятору найти типы Customer (Клиент) и Hotel (Гостиница), используемые в AcmeGui и затем скомпоновать приложение. Они не диктуют, куда нужно поместить динамически подключаемые библиотеки (DLL) при развертывании проекта. Обратите также внимание на ссылки на системные сборки, такие как System.dll. Посмотрев на свойства ссылок, вы узнаете, где расположена сборка.
Однако в пределах существующего решения AcmeGui довольно просто создать проект на основе шаблона Managed C++ Class Library (Управляемый C++ на основе Библиотеки классов). Щелкните правой кнопкой мыши на узле Solution AcmeGui (Решение AcmeGui) в Solution Explorer (Поиск решения, Проводник решения), затем выберите Add => New Project (Добавить => Новый проект). Выберите тип Visual C++ Project (Проект на Visual C++), потом выберите шаблон Managed Class Library (Библиотека Управляемых классов), найдите CaseStudy, и назовите проект Customer (Клиент). В Visual Studio.NET выберите Class Library (Библиотека классов) в New Project Wizard (Мастер создания проектов), затем укажите местоположение и имя, после чего щелкните на ОК. Проект Customer (Клиент) находится в папке C:\OI\NetCpp\Chap7\CaseStudy. То же самое сделайте для другой сборки — Hotel (Гостиница), которая представляет собой динамически подключаемую библиотеку (DLL). Чтобы установить в вашем проекте ссылку на каждую из сборок, которые в данном примере представляют собой динамически подключаемые библиотеки (DLL), используйте оператор ttusing. Конечно, библиотеки должны быть созданы прежде, чем на них можно будет сослаться.
На рис. 7.10 показан верхний уровень, который вы увидите, когда откроете сборку Customer.dll в ILDASM и выполните двойной щелчок на пространстве имен 01 .NetCpp.Acme. Вы видите точки входа для декларации MANIFEST (МАНИФЕСТ, ДЕКЛАРАЦИЯ), классов Customers (Клиенты) и Customer (Клиент), интерфейса ICustomer и типа CustomerListltem. Если щелкнуть на кнопке плюс (+), точка входа будет развернута.
Чтобы рассмотреть декларацию, дважды щелкните на узле MANIFEST (МАНИФЕСТ, ДЕКЛАРАЦИЯ), показанном на рис. 7.10, и в результате будет отображена информация, относящаяся к декларации (рис. 7.11). Некоторые числа будут другими, если вы заново создали какой-либо из примеров, или имеете более позднюю версию .NET.
Декларация содержит информацию о зависимостях и содержимом сборки. Видно, что декларация для Customer (Клиент) содержит, среди других, следующую внешнюю зависимость:

.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 EO 89 )
.hash = (09 BB ВС 09 EF 6D 9B F4 F2 CC IB 55 ... EF 77 )
.ver 1:0:2411:0
}



Рис. 7.9. Ссылки, показываемые Solution Explorer (Поиск решения, Проводник решения) для AcmeGui

Если вы заново создали какой-нибудь из компонентов, то, конечно, номера компоновки и пересмотра будут отличаться.



Рис. 7.10. Верхний уровень представления (вида), отображаемого утилитой ILDASM для компонента Customer (Клиент)



Рис. 7.11. ILDASM показывает декларацию Customer .dll

Инструкция метаданных .assembly extern mscorlib указывает, что сборка Customer (Клиент) использует (и поэтому зависит от нее) стандартную сборку mccorlib. dll, которая нужна для всего управляемого кода. Когда в сборке встречается ссылка на другую сборку, вы увидите инструкцию метаданных . assembly extern. Если открыть AcmeGui в ILADASM, то в декларации вы увидите зависимости от нескольких других сборок, включая зависимости от сборок Customer (Клиент) и Hotel (Гостиница), а также зависимости от сборки System. Drawing (Система.Рисунок).

.assembly extern Customer
{
.publickeytoken = (8В ОЕ 61 2D 60 BD EO CA )
.ver 1:0:641:33530
}
.assembly extern Hotel
{
.publickeytoken = (CF OB C2 2F 8E 2C 15 22 )
.ver 1:0:641:33536
}
.assembly extern System.Drawing
{
.publickeytoken = (BO 3F 5F 7F 11 D5 OA ЗА )
.ver 1:0:2411:0
}

Сборка System. Drawing (Система.Рисунок) — общедоступная сборка, которую можно легко найти в каталоге \WINNT\Assembly с помощью Проводника Windows (Windows Explorer). Общедоступная сборка mscorlib не развернута в кэше сборки. Microsoft сделала единственное исключение для этой сборки, потому что mscorlib так близко связана с механизмом общеязыковой среды выполнения CLR (mscorwks), что она устанавливается в соответствующем каталоге установки (\WINNT\Microsoft.NET\Framework) для текущей версии .NET.
В общедоступной сборке System. Drawing (Система.Рисунок) инструкция метаданных .publickeytoken = (ВО 3F 5F 7F 11 D5 ОА ЗА) содержит лексему открытого ключа, представляющую собой самые младшие 8 байтов хэш-кода открытого ключа, который соответствует своему секретному ключу, принадлежащему автору сборки System. Drawing (Система.Рисунок). Эта лексема открытого ключа не может фактически непосредственно использоваться для того, чтобы подтвердить подлинность автора сборки System. Drawing (Система.Рисунок). Однако первоначальный открытый ключ, указанный в декларации System. Drawing (Система.Рисунок) может использоваться, чтобы математически проверить, что для того, чтобы подписать сборку System. Drawing (Система.Рисунок) в цифровой форме на самом деле использовался соответствующий секретный ключ. Поскольку Microsoft создала System.Drawing.dll, лексема открытого ключа, приведенная выше, принадлежит, несомненно, Microsoft.



Установка и развертывание проектов

Сборки могут быть развернуты как обычные автономные файлы, содержащие двоичный код (т.е. динамически подключаемые библиотеки (DLL) или исполняемые файлы), либо при этом могут использоваться файлы CAB, MSI, или MSM. Файл CAB — файл с расширением . cab для имени файла. Он используется, чтобы сжать и объединить другие файлы в один удобный управляемый файл. Хотя файлы CAB могут использоваться для общих целей, они традиционно используются для инсталляции с компакт-диска и загрузки из Web. Файлы MSI и MSM — файлы инсталлятора Microsoft Windows Installer; имена таких файлов имеют расширения .msi и .msm. Файлы MSI (и, косвенно, файлы MSM) используются программой инсталляции в Windows— Msiexec.exe — для того, чтобы развернуть автономные приложения и компоненты многократного использования.
Файлы MSI — пакеты инсталляции программы Microsoft Windows Installer; имена таких файлов имеют расширение .msi. Файлы MSM — модули слияния; имена таких файлов имеют расширение . msm. Инсталлятор Windows (Windows Installer) поддерживает инсталляцию программ, исправление, обновление и удаление. Пакеты инсталлятора Windows (Windows Installer) — независимые (отдельные) файлы базы данных, содержащие информацию об инсталляции, используемую службой инсталлятора Windows (Windows Installer). Несколько упрощая реальное положение дел, можно сказать, что файл MSM имеет внутреннюю структуру, которая подобна структуре файла MSI. К сожалению, инсталлятор Windows (Windows Installer) не может использовать файл MSM непосредственно, так как ему нужны некоторые важные таблицы базы данных. Поэтому для использования в фактическом инсталляционном сеансе файл MSM должен быть слит (объединен с другими файлами) в файл MSI Однако файлы MSM полезны для того, чтобы выделить общедоступную инсталляционную информацию в независимый пакет, который может быть затем слит (объединен с другими) во многие другие пакеты MSI
Инсталляцию может выполнять инсталлятор Windows (Windows Installer) или Internet Explorer Кроме того, чтобы выполнить инсталляцию, можно просто вручную скопировать сборки и входящие в их состав файлы Чтобы помочь программисту разработать установку и решения развертывания, в Visual Studio NET предусмотрено несколько шаблонов и мастеров, генерирующих проекты установки стартера Эти инструментальные средства доступны в диалоговом окне New Project (Новый проект) под узлом Setup and Deployment Projects (Установка и Развертывание проектов), рис. 7.12 Как видно из рисунка, для того, чтобы генерировать установку стартера и проекты развертывания, предусмотрены следующие шаблоны

Cab Project (Проект Cab), Setup Project (Проект установки), Setup Wizard (Мастер установки), Merge Module Project (Проект модуля слияния), Web Setup Project (Проект установки по Сети)

CAB Project (Проект CAB)

Проект CAB (CAB project) создает САВ-файл, содержащий любое количество других файлов, которые могут использоваться для традиционных целей развертывания Файлы CAB использовались для упаковки старых компонентов, построенных на основе модели компонентных объектов Microsoft (COM), для развертывания их по Internet Кроме того, они использовались в традиционных инсталляционных программах, которые выполняли установку с компакт-дисков Теперь файлы CAB также могут использоваться для того, чтобы упаковать управляемый код, однако при развертывании в NET файл CAB может содержать только одну сборку, причем он должен иметь то же самое имя, что и содержащаяся в нем сборка, но с расширением cab Например, сборка SomeComponent.dll может содержаться в файле CAB с именем SomeComponent. cab



Рис. 7.12. Шаблоны установки и развертывания проектов (Setup and Deployment Projects)



Проект установки (Setup Project)

Шаблон Setup Project (Проект установки) создает файл msi, используемый инсталлятором Windows (Windows Installer) для настольного или распределенного приложения Проект установки (Setup Project) не предназначен для развертывания приложений, основанных на технологии WWW, так как для этой цели используется специализированный проект сетевой установки (Web setup project) Проект установки (Setup Project) генерирует программу, которая устанавливает приложение на целевую машину Вы можете создать проекты установки в пределах того же самого решения, которое содержит другие развертываемые проекты В решении с несколькими уровнями вы можете создать один проект установки для каждого проекта, который должен быть развернут на конкретной целевой вычислительной машине Например, в простом решении с тремя звеньями, вероятно, будет три проекта развертывания Два простых проекта развертывания предназначены для установки клиента и сервера Третий проект развертывания будет заботиться о более сложной бизнес-логике среднего звена Кроме того, могли бы потребоваться дополнительные проекты развертывания, если бы решение было очень сложным или в стратегию развертывания были включены модули слияния
Чтобы создать проект установки, выберите FileONew (Файл<> Создать), затем выберите Project (Проект) В диалоговом окне New Project (Новый проект) в качестве типа проекта (Project Type) выберите Setup and Deployment Projects (Проекты установки и развертывания) Наконец, выберите в качестве шаблона (Template) Setup Project (Проект установки), укажите имя и местоположение, а затем щелкните на ОК Результат показан на рис. 7.13. — вы видите открытые Solution Explorer (Поиск решения, Проводник решения) и File System Editor (Редактор файловой системы)



Рис. 7.13. Применение Solution Explorer (Поиск решения, Проводник решения) и File System Editor (Редактор файловой системы) в проекте установки

Создав начальный проект установки с помощью шаблона Setup Project (Проект установки), его можно разрабатывать далее, используя File System Editor (Редактор файловой системы). Редактор файловой системы (File System Editor) позволяет перетащить и опустить или скопировать и вставить файлы, которые будут развернуты проектом развертывания установки, и управлять их адресатами на целевой машине. Сразу после запуска редактор файловой системы (File System Editor) показывает начальный список папок-получателей (результирующих папок), в которые будут развернуты компоненты, причем к этому списку вы можете добавить ваши собственные папки.
Создав файл MSI, вы можете использовать инсталлятор Windows (Windows Installer), как показано в следующей командной строке:

Msiexec /i SomeSetup.msi

Затем запускается программа Windows Installer (Инсталлятор Windows). Эта программа отображает ряд инсталляционных диалогов. После того, как инсталлятор Windows (Windows Installer) завершит развертывание, вы можете попробовать выполнить установленное приложение, чтобы проверить, что инсталляция была завершена успешно. Если выполнить ту же самую команду, Msiexec /i SomeSetup.msi, то она обнаружит, что приложение уже существует, и предложит вам восстановить инсталляцию или деинсталлировать приложение.



Merge Module Project (Проект модуля слияния)

Проект модуля слияния упаковывает многократно используемую информацию об установке; она может храниться независимо, а затем объединяется в качестве общедоступного инсталляционного пакета с другими инсталляционными пакетами. Проект модуля слияния генерирует модуль слияния— файл .msm, который может быть объединен в файлы .msi. Это позволяет совместно использовать общие сборки, связанные (входящие в состав) файлы, значения системного реестра и функциональные возможности установки для нескольких приложений.
Чтобы запустить Merge Module Project Wizard (Мастер проектов модуля слияния), выберите File => New (Файл => Создать), затем выберите Project (Проект). В диалоговом окне New Project (Новый проект) в качестве типа проекта (Project Type) выберите Setup and Deployment Projects (Установка и Развертывание проектов, Проекты Setup и Deployment). Наконец, выберите в качестве шаблона (Template) Merge Module Project " Wizard (Мастер проектов модуля слияния), укажите имя и местоположение, а затем щелкните на ОК.
Как правило, файл MSI предназначен для использования конечным пользователем с целью установить законченное решение за один простой сеанс развертывания. Файл MSM, напротив, обычно предназначается для других разработчиков, которые хотят использовать разработанные вами компоненты в их собственных проектах установки. Эти разработчики могут объединить ваш файл MSM в их собственный файл MSI для того, чтобы развернуть ваши компоненты в их средах испытания и разработки, а также для их окончательного конечного пользователя. Конечные пользователи не должны получать какие-либо файлы MSM, так как Windows Installer (Инсталлятор Windows) не может использовать их непосредственно, а сами они не являются очень дружественными по отношению к пользователю.
Чтобы добавить существующий проект модуля слияния к проекту установки, создайте или откройте проект установки и выберите Select FileOAdd Project (Выбрать файл^Добавить проект), а затем выберите Existing Project. В диалоговом окне Add Existing Project (Добавить существующий проект) найдите местоположение нужного проекта модуля слияния, выберите связанный (т.е. входящий в состав) файл . vdp проекта развертывания, а потом щелкните на Open (Открыть).
Мы только добавили проект модуля слияния к решению. Теперь мы должны добавить его к самому проекту установки. Выберите проект установки и вызовите Add: Project Output (Добавить: Вывод проекта), а затем в появившемся диалоге выберите проект модуля слияния.



Развертывание по сети

При развертывании по сети у клиента используется Internet Explorer для того, чтобы по требованию от Web-сервера .NET автоматически загрузить сборки, упакованные как ЕХЕ-файлы, динамически подключаемые библиотеки (DLL), или файлы CAB. Чтобы управлять процессом связывания, файлы HTML могут динамически развернуть сборки, а также файлы конфигурации. При развертывании по сети затребованные сборки загружаются в глобальный кэш загрузки сборок клиента.
Тэг <object> (<объект>) используется для загрузки и установки сборок по относительному или абсолютному унифицированному указателю информационного ресурса (URL). В приведенном ниже примере используется относительный унифицированный указатель информационного ресурса (URL), поэтому сборка располагается относительно того каталога Web-сервера, в котором содержится файл HTML:

<object
id="SomeComponent"
classid="./SomeDirectory/MyComponent.dll#SomeClass">
</object>

В приведенном ниже примере используется абсолютный унифицированный указатель информационного ресурса (URL), поэтому сборка располагается на указанном Web-сервере:

<object
id="SomeComponent"
classid="http://www.acme.com/MyComponent.dll#SomeClass">
</object>

По умолчанию IE создает отдельную прикладную область для каждого встреченного им Web-узла. Прикладная область— средство .NET, напоминающее масштабируемый упрощенный процесс. Прикладная область эффективно обеспечивает изоляцию ошибки без накладных расходов, неизбежных при выполнении множества настоящих процессов. Каждая из этих прикладных областей может при необходимости иметь свой собственный файл конфигурации, чтобы управлять связыванием и защитой. Кроме того, в файле конфигурации можно определить изолированную прикладную область для индивидуальных приложений на том же самом Web-сервере. Каждый файл HTML, который определяет тот же самый файл конфигурации, будет помещен в ту же самую прикладную область.



основе модели компонентных объектов Microsoft

Развертывание очень важно, и составляет одну из главных стадий в разработке программного обеспечения. Если оно не выполнено должным образом, все усилия, потраченные на разработку, становятся пустой тратой времени и денег. К счастью, благодаря сборкам, которые в .NET являются основными единицами развертывания, легко решаются многие сложные проблемы, связанные с развертыванием. Просто создавая динамически загружаемые библиотеки, вы можете получить многократно используемые компоненты без всяческих трудностей, связанных с созданием и установкой компонентов, построенных на основе модели компонентных объектов Microsoft (COM).
Частное развертывание сборки представляет собой не что иное, как просто копирование сборки в тот же самый каталог, в котором расположено приложение-клиент. Для общедоступного развертывания сборки нужно создать строгое имя сборки, а затем развернуть ее в глобальном кэше сборок. Утилита Strong Name (Sn. ехе) создает строгое имя сборки. Потом утилита Gacutil.exe (Global Assembly Cache utility) или .NET Admin Tool (Инструмент администрирования .NET) может использоваться для того, чтобы развернуть общедоступную сборку в глобальном кэше сборок. Кроме того, можно объединить несколько модулей в единую логическую сборку. Чтобы это сделать, необходимо создать отдельные модули без информации, относящейся к декларации, а затем можно сгенерировать модуль декларации для всех модулей, которые включаются в сборку. Мастера CAB и установки, имеющиеся в среде разработки Visual Studio.NET, очень полезны для того, чтобы создать установку стартера и проекты развертывания, — это позволяет сэкономить много времени, которого всегда не хватает на разработку большой системы.